package com.quanyue.music.order.service.impl;

import com.github.binarywang.wxpay.bean.request.WxPayRefundQueryRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.service.WxPayService;
import com.quanyue.auto.pay.PayContext;
import com.quanyue.auto.pay.PayType;
import com.quanyue.auto.util.AssertUtils;
import com.quanyue.auto.util.StringUtils;
import com.quanyue.auto.util.TimeUtils;
import com.quanyue.music.domain.constant.PayResult;
import com.quanyue.music.domain.constant.RefundStatus;
import com.quanyue.music.domain.entity.Order;
import com.quanyue.music.domain.entity.User;
import com.quanyue.music.order.service.OrderService;
import com.quanyue.music.order.service.PayService;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class PayServiceImpl implements PayService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private WxPayService wxPayService;
    @Autowired
    private OrderService orderService;

    @Override
    public Map<String, Object> pay(Integer merchantNo, BigDecimal price, String orderNo, User user, String userIp) {
        Map<String, Object> result;
        if (price.compareTo(BigDecimal.ZERO) == 0) {
            result = new HashMap<>(1);
            result.put("complete", true);
        } else {
            BigDecimal amount = price.multiply(BigDecimal.valueOf(100));
            result = wechatPay(merchantNo, "订单号" + orderNo + "-支付", orderNo + amount.intValue(), amount, user, userIp);
        }
        return result;
    }

    @Override
    public String refund(Integer merchantNo, String orderNo, String payId, BigDecimal totalFee, BigDecimal refundFee) {
        WxPayRefundRequest refundRequest = new WxPayRefundRequest();
        refundRequest.setTransactionId(payId);
        refundRequest.setOutRefundNo(orderNo);
        refundRequest.setTotalFee(totalFee.multiply(BigDecimal.valueOf(100)).intValue());
        refundRequest.setRefundFee(refundFee.multiply(BigDecimal.valueOf(100)).intValue());
        try {
            PayContext.setMerchantNo(merchantNo);
            PayContext.setPayType(PayType.REFUND);
            WxPayRefundResult result = wxPayService.refund(refundRequest);
            if (result.getResultCode().equals("SUCCESS")) {
                return result.getRefundId();
            } else {
                throw new AssertUtils.ValidateException(result.getErrCodeDes());
            }
        } catch (WxErrorException e) {
            logger.error("支付失败", e);
            throw new AssertUtils.ValidateException(e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlePayConfirm(WxPayOrderNotifyResult result) throws WxErrorException {
        this.handleOrder(result);
    }

    @Override
    public void handleRefundConfirm(WxPayRefundResult refundResult) throws WxErrorException {
        WxPayRefundQueryRequest refundQuery = new WxPayRefundQueryRequest();
        refundQuery.setRefundId(refundResult.getRefundId());
        WxPayRefundQueryResult result = wxPayService.refundQuery("", "", "", refundResult.getRefundId());
        if (result.getReturnCode().equals("SUCCESS")) {
            for (WxPayRefundQueryResult.RefundRecord refundRecord : result.getRefundRecords()) {
                if (refundResult.getOutRefundNo().equals(refundRecord.getOutRefundNo())) {
                    orderService.afterRefundComplete(refundRecord.getOutRefundNo(), RefundStatus.valueOf(refundRecord.getRefundStatus()));
                }
            }
        }
    }

    private void handleOrder(WxPayOrderNotifyResult result) throws WxErrorException {
        String orderNo = result.getOutTradeNo();
        if (orderNo.length() > 20) {
            orderNo = orderNo.substring(0, 20);
        }
        Order order = orderService.findOrderByNo(orderNo);
        AssertUtils.notNull(order, "订单不存在");
        WxPayOrderQueryResult orderResult;
        PayContext.setMerchantNo(10100);
        if (StringUtils.isNotBlank(result.getTransactionId()) && StringUtils.isNotBlank(result.getOutTradeNo())) {
            orderResult = wxPayService.queryOrder(result.getTransactionId(), "");
        } else {
            orderResult = wxPayService.queryOrder(result.getTransactionId(), result.getOutTradeNo());
        }
        logger.info("Play返回值：{}", orderResult.getResultCode() + ">>>" + orderResult.getReturnCode());
        if (orderResult.getReturnCode().equals("SUCCESS")
                && orderResult.getResultCode().equals("SUCCESS")) {
            orderService.afterPayComplete(orderNo, orderResult.getTransactionId(), PayResult.valueOf(orderResult.getTradeState()));
        }

    }

    private Map<String, Object> wechatPay(Integer merchantNo, String body, String tradeNo, BigDecimal leftFee, User user, String ip) {
        WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
        orderRequest.setBody(body);
        orderRequest.setOutTradeNo(tradeNo);
        orderRequest.setTotalFee(leftFee.intValue());
        orderRequest.setOpenid(user.getOpenId());
        orderRequest.setSpbillCreateIp(ip);
        LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        orderRequest.setTimeStart(TimeUtils.toDateTimeString(now));
        orderRequest.setTimeExpire(TimeUtils.toDateTimeString(now.plusMinutes(30)));
        orderRequest.setNotifyURL("http://www.admin.liangzimusic.com/wechat/pay/confirm");
        try {
            PayContext.setPayType(PayType.PAY);
            PayContext.setMerchantNo(merchantNo);
            Map<String, Object> result = wxPayService.getPayInfo(orderRequest)
                    .entrySet()
                    .stream()
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            result.put("complete", false);
            return result;
        } catch (WxErrorException e) {
            logger.error("支付失败", e);
            throw new AssertUtils.ValidateException(e.getMessage());
        }
    }
}
